from umodbus.serial import Serial as ModbusRTUMaster
from umodbus import functions as modbus

from machine import Pin
import os
from umodbus import version


def getHardwareInfo():
    try:
        os_info = os.uname()
        print("MicroPython infos: {}".format(os_info))
        print("Used micropthon-modbus version: {}".format(version.__version__))
        if "esp32" in os_info:
            uartid = 1
            rtupins = (14, 45)  # (TX, RX)
            depin = 21
        elif "rp2" in os_info:
            uartid = 1
            rtupins = (Pin(4), Pin(5))  # (TX, RX)
            depin = 15
    except AttributeError:
        pass
    except Exception as e:
        raise e
    return (uartid, rtupins, depin)


class ModbusMaster:
    def __init__(self, baudrate, databits=8, stopbits=1, parity=None):
        uart_id, rtu_Pins, de_pin = getHardwareInfo()
        self.host = ModbusRTUMaster(
            pins=rtu_Pins,  # given as tuple (TX, RX)
            baudrate=baudrate,  # optional, default 9600
            data_bits=databits,  # optional, default 8
            stop_bits=stopbits,  # optional, default 1
            parity=parity,  # optional, default None
            ctrl_pin=de_pin,  # optional, control DE/RE
            uart_id=uart_id,  # optional, default 1, see port specific docs
        )

    def read32BitFloatHoldingRegister(self, slave_addr, hrAddr):
        try:
            register_value = self.host.read_holding_registers(
                slave_addr=slave_addr,
                starting_addr=hrAddr,
                register_qty=2,
                signed=False,
            )
        except Exception as e:
            print("Error reading holding registers: {}".format(e))
            return ("error", 0)
        else:
            # print("Status of HREG {}: {}".format(hreg_address, register_value))
            temBinData = modbus.int_to_bin(
                (register_value[0] * 0x10000) + register_value[1]
            )
            floatData = modbus.bin_to_float(temBinData)
            return ("ok", floatData)
    
    def write32BitFloatHoldingRegister(self, slave_addr, hrAddr, value):
        try:
            binData = modbus.float_to_bin(value)
            status = self.host.write_multiple_registers(
                slave_addr=slave_addr,
                starting_address=hrAddr,
                register_values=[int(binData[0:16], 2), int(binData[16:], 2)],
            )
        except Exception as e:
            print("Error writing holding registers: {}".format(e))
            return ("error", e)
        else:
            # print("Status of HREG {}: {}".format(hreg_address, register_value))
            return ("ok", status)
    
    def readHoldingRegister(self, slave_addr, hrAddr, hrLen):
        try:
            register_value = self.host.read_holding_registers(
                slave_addr=slave_addr,
                starting_addr=hrAddr,
                register_qty=hrLen,
                signed=False,
            )
        except Exception as e:
            print("Error reading holding registers: {}".format(e))
            return ("error", 0)
        else:
            # print("Status of HREG {}: {}".format(hreg_address, register_value))
            return ("ok", register_value)

    def writeSingleRegister(self, slave_addr, hrAddr, value):
        try:
            status = self.host.write_single_register(
                slave_addr=slave_addr,
                register_address=hrAddr,
                register_value=value,
            )
        except Exception as e:
            print("Error writing holding registers: {}".format(e))
            return ("error", e)
        else:
            # print("Status of HREG {}: {}".format(hreg_address, register_value))
            return ("ok", status)
        
    def writeMultipleRegisters(self, slave_addr, hrAddr, value):
        try:
            status = self.host.write_multiple_registers(
                slave_addr=slave_addr,
                starting_address=hrAddr,
                register_values=value,
            )
        except Exception as e:
            print("Error writing holding registers: {}".format(e))
            return ("error", e)
        else:
            # print("Status of HREG {}: {}".format(hreg_address, register_value))
            return ("ok", status)

    def readCoil(self, slave_addr, coilAddr, coilLen):
        try:
            coil_value = self.host.read_coils(
                slave_addr=slave_addr,
                starting_addr=coilAddr,
                coil_qty=coilLen,
            )
        except Exception as e:
            print("Error reading coils: {}".format(e))
            return ("error", 0)
        else:
            return ("ok", coil_value)

    def writeSingleCoil(self, slave_addr, coilAddr, value):
        try:
            status = self.host.write_single_coil(
                slave_addr=slave_addr,
                output_address=coilAddr,
                output_value=value,
            )
        except Exception as e:
            print("Error writing coils: {}".format(e))
            return ("error", e)
        else:
            return ("ok", status)

    def writeMultipleCoils(self, slave_addr, coilAddr, value):
        try:
            status = self.host.write_multiple_coils(
                slave_addr=slave_addr,
                starting_address=coilAddr,
                output_values=value,
            )
        except Exception as e:
            print("Error writing coils: {}".format(e))
            return ("error", e)
        else:
            return ("ok", status)

    def readDiscreteInput(self, slave_addr, inputAddr, inputLen):
        try:
            input_value = self.host.read_discrete_inputs(
                slave_addr=slave_addr,
                starting_addr=inputAddr,
                input_qty=inputLen,
            )
        except Exception as e:
            print("Error reading discrete inputs: {}".format(e))
            return ("error", 0)
        else:
            return ("ok", input_value)
        
    def readInputRegister(self, slave_addr, inputAddr, inputLen):
        try:
            input_value = self.host.read_input_registers(
                slave_addr=slave_addr,
                starting_addr=inputAddr,
                register_qty=inputLen,
            )
        except Exception as e:
            print("Error reading input registers: {}".format(e))
            return ("error", 0)
        else:
            return ("ok", input_value)